package com.mages.slope.service;

import com.mages.slope.REST.SlopeRESTTemplate;
import com.mages.slope.util.CommonUtil;
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.encoder.datastore.GSGeoTIFFDatastoreEncoder;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Random;

@Component
public class SlopeService {
    private String inputRasterURL;
    private String outputGeoserverURL;
    private String uuid;
    private String clientName;
    private String clientIP;
    // TODO 发布时需修改以下配置
    private static String cacheDir;
    private static String user;
    private static String password;
    private static String workspace;
    private static String pythonInterpreter;
    private static String scriptPath;
    private static String logDir;
    private static double price;
    private static double reliability;
    private static double security;

//    initial
    public SlopeService() {
    }

    public SlopeService(String inputRasterURL, String outputGeoserverURL) {
        this.inputRasterURL = inputRasterURL;
        this.outputGeoserverURL = outputGeoserverURL;
    }

    public SlopeService(String inputRasterURL, String outputGeoserverURL, String uuid, String clientName, String clientIP) {
        this.inputRasterURL = inputRasterURL;
        this.outputGeoserverURL = outputGeoserverURL;
        this.uuid = uuid;
        this.clientName = clientName;
        this.clientIP = clientIP;
    }

//set and get
    @Value("${qos.price}")
    public void setPrice(double price) {
        SlopeService.price = price;
    }

    @Value("${qos.reliability}")
    public void setReliability(double reliability) {
        SlopeService.reliability = reliability;
    }

    @Value("${qos.security}")
    public void setSecurity(double security) {
        SlopeService.security = security;
    }

    @Value("${service.cache_dir}")
    public void setCacheDir(String cacheDir) {
        SlopeService.cacheDir = cacheDir;
    }

    @Value("${geoserver.user}")
    public void setUser(String user) {
        SlopeService.user = user;
    }

    @Value("${geoserver.password}")
    public void setPassword(String password) {
        SlopeService.password = password;
    }

    @Value("${geoserver.workspace}")
    public void setWorkspace(String workspace) {
        SlopeService.workspace = workspace;
    }

    @Value("${service.python_interpreter}")
    public void setPythonInterpreter(String pythonInterpreter) {
        SlopeService.pythonInterpreter = pythonInterpreter;
    }

    @Value("${service.script_path}")
    public void setScriptPath(String scriptPath) {
        SlopeService.scriptPath = scriptPath;
    }

    @Value("${service.log_dir}")
    public void setLogDir(String logDir) {
        SlopeService.logDir = logDir;
    }

    public String getInputRasterURL() {
        return inputRasterURL;
    }

    public void setInputRasterURL(String inputRasterURL) {
        this.inputRasterURL = inputRasterURL;
    }

    public String getOutputGeoserverURL() {
        return outputGeoserverURL;
    }

    public void setOutputGeoserverURL(String outputGeoserverURL) {
        this.outputGeoserverURL = outputGeoserverURL;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public String getClientIP() {
        return clientIP;
    }

    public void setClientIP(String clientIP) {
        this.clientIP = clientIP;
    }

//    创建数据的下载和存储目录
    @PostConstruct
    public void checkWorkdir() {
        // check whether dir/file exists
//        System.out.println("check workdir");
//        System.out.println(cacheDir);
//        System.out.println(logDir);
        // cache dir
        File file = new File(String.format("%s/download", cacheDir));
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: cache/download");
                return;
            }
        }
        file = new File(String.format("%s/result", cacheDir));
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: cache/result");
                return;
            }
        }
        // log dir
        file = new File(logDir);
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: log");
            }
        }
    }

//    服务核心处理部分-----------------------------
    public SlopeRESTTemplate RunSlope() {
        Date date = new Date();
        SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
        String currentTime = timeFormat.format(date);
        // System.out.println(currentTime);
        String today = currentTime.split("_")[0];
        long startTime = System.currentTimeMillis();// 开始运行时间
        // 添加授权验证
        String auth = Base64.getEncoder().encodeToString(String.format("%s:%s", user, password).getBytes(StandardCharsets.UTF_8));
        String metaData = CommonUtil.MetaData(inputRasterURL, auth);
        String serverIP = "";
        String serverName = "";
        try {
            InetAddress serverIPAddr = InetAddress.getLocalHost();
            serverIP = serverIPAddr.getHostAddress();
            serverName = serverIPAddr.getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        String logLine = String.format("RequestUUID:%s;LogTime:%s;Status:Running;ClientName:%s;ClientIP:%s;ServerName:%s;ServerIP:%s;MetaData:%s;StartTime:%d\n", uuid, currentTime, clientName, clientIP, serverName, serverIP, metaData, startTime);

        int flag = 0;
        // 记录日志：初始状态
        String logFilePath = String.format("%s/Slope%s.log", logDir, today);
        File fLog = new File(logFilePath);
        if (!fLog.exists()) {
            System.out.println("log file not found! try to create...");
            try {
                fLog.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("log file found! append to the end...");
        }
        try {
            BufferedWriter logWriter = new BufferedWriter(new FileWriter(fLog, true));
            logWriter.write(logLine);
            logWriter.flush();
            logWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 文件下载
        // 从inputRasterURL中解析文件名称
        int dataNameStartIdx = inputRasterURL.indexOf("coverageId") + workspace.length() + 12;
        int dataNameEndIdx = inputRasterURL.indexOf("format") - 1;
        String dataName = String.format("%s.tif", inputRasterURL.substring(dataNameStartIdx, dataNameEndIdx));
        // 文件缓存路径设置
        String dataPath = String.format("%s/download/%s", cacheDir, dataName);
        String storeName = String.format("Slope%s", uuid);
        String resultPath = String.format("%s/result/%s.tif", cacheDir, storeName);
        // TODO cache文件夹缓存清理设置
        try {
            File file = new File(dataPath);
            if (!file.exists()) {
                file.createNewFile();
            }
            URL url = new URL(inputRasterURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(6000);
            conn.setReadTimeout(6000);
            conn.setRequestProperty("Authorization", String.format("Basic %s", auth));
            int code = conn.getResponseCode();
            if (code != HttpURLConnection.HTTP_OK) {
                throw new Exception("文件读取失败！");
            }
            DataInputStream in = new DataInputStream(conn.getInputStream());
            DataOutputStream out = new DataOutputStream(Files.newOutputStream(Paths.get(dataPath)));
            byte[] buffer = new byte[2048];
            int count = 0;
            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = 0;
        }

        // 坡度处理
        try {
            Process process;
            // 坡度脚本调用命令
            String command = String.format("%s %s %s %s", pythonInterpreter, scriptPath, dataPath, resultPath);
            process = Runtime.getRuntime().exec(command);
            int exitCode;
            try {
                exitCode = process.waitFor();
                if (exitCode == 0) {
                    System.out.println("程序执行成功！");
                } else {
                    System.err.println("程序执行出错：exit=" + exitCode);
                }
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        // 上传处理结果到geoserver中
        String outputRasterURL = "";
        try {
            // 检查工作区是否存在
            URL geoserver = new URL(outputGeoserverURL);
            GeoServerRESTManager manager = new GeoServerRESTManager(geoserver, user, password);
            GeoServerRESTPublisher publisher = manager.getPublisher();
            List<String> workspaces = manager.getReader().getWorkspaceNames();
            if (!workspaces.contains(workspace)) {
                boolean whetherWS = publisher.createWorkspace(workspace);
                System.out.printf("create workspace : %b%n", whetherWS);
            } else {
                System.out.printf("workspace(%s) existed!%n", workspace);
            }
            // 上传数据……
            GSGeoTIFFDatastoreEncoder encoder = new GSGeoTIFFDatastoreEncoder(storeName);
            encoder.setWorkspaceName(workspace);
            encoder.setUrl(new URL(String.format("file:%s", resultPath)));
            boolean whetherStore = manager.getStoreManager().create(workspace, encoder);
            System.out.printf("create datastore : %b%n", whetherStore);
            boolean whetherPublish = manager.getPublisher().publishGeoTIFF(workspace, storeName, new File(resultPath));
            System.out.printf("publish : %b%n", whetherPublish);
            outputRasterURL = String.format("%sows?service=WCS&version=2.0.1&request=GetCoverage&coverageId=%s:%s&format=image/tiff", outputGeoserverURL, workspace, storeName);
            if (whetherPublish) {
                flag = 1;
            } else {
                flag = 0;
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = 0;
        }

        // 日志设置
        // 程序执行时间
        long endTime = System.currentTimeMillis();
        long runningTime = endTime - startTime;
        System.out.printf(String.format("程序运行时间：%dms%n", runningTime));
        // 写入日志文件
//        double price = 8, reliability = 8, security = 8;
        int reputation = 0;
        double minCost = 0, maxCost = 3;
        Random random = new Random();
        System.out.println(String.format("%f %f %f", reliability, security, price));
        double final_reliability = reliability + random.nextDouble();
        double final_security = security + random.nextDouble();
        double cost = price + minCost + (maxCost - minCost) * random.nextDouble();
        if (flag == 1) {
            if (runningTime < 12000) {
                reputation = 5;
            } else if (runningTime < 14000) {
                reputation = 4;
            } else if (runningTime < 15000) {
                reputation = 3;
            } else if (runningTime < 16000) {
                reputation = 2;
            } else if (runningTime < 20000) {
                reputation = 1;
            }
        }

        String finishedLogLine = logLine.substring(0, logLine.length() - 1) + String.format(";EndTime:%d;RunningTime:%d;Succeed:%d;Availability:%d;Reliability:%f;Cost:%f;Reputation:%d;Security:%f\n", endTime, runningTime, flag, flag, final_reliability, cost, reputation, final_security);
        if (flag == 0) {
            finishedLogLine = finishedLogLine.replaceAll("Status:Running", "Status:Error");
        } else {
            finishedLogLine = finishedLogLine.replaceAll("Status:Running", "Status:Completed");
        }
        // 更新日志文件
        boolean whetherLog = CommonUtil.ReplaceLogByUUID(logFilePath, uuid, finishedLogLine);

        return new SlopeRESTTemplate(outputRasterURL, finishedLogLine, whetherLog);
    }

}
